﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using static System.Net.WebRequestMethods;

namespace DotNetCommon
{
    /// <summary>
    /// 基于 <c>AsyncLocal</c> 封装的代码执行上下文, 用于在异步代码中传递数据, 示例:
    /// <code>
    /// public async Task ProcessRequest()
    /// {
    ///     // 从 request 中读取用户Id
    ///     var id = 1;
    ///     // 将用户Id存储到上下文中
    ///     var dic = new ConcurrentDictionary&lt;object, object>();
    ///     dic["id"] = id;
    ///     // 业务逻辑
    ///     await RunningContext.RunInContextAsync(DoBusiness, dic);
    /// }
    /// 
    /// public async Task DoBusiness()
    /// {
    ///     var userId = RunningContext.Current.GetValue&lt;int>("id");
    ///     //...
    /// }
    /// </code>
    /// </summary>
    /// <remarks>参照: <seealso href="https://blog.csdn.net/u010476739/article/details/121395350"/></remarks>
    public class RunningContext
    {
        private RunningContext(IDictionary<object, object> dic)
        {
            _id = Interlocked.Increment(ref _counter);
            this.Dic = dic;
        }
        private static AsyncLocal<RunningContext> Context = new AsyncLocal<RunningContext>();
        /// <summary>
        /// 当前代码执行的上下文环境
        /// </summary>
        public static RunningContext Current
        {
            get
            {
                Context.Value ??= new RunningContext(new ConcurrentDictionary<object, object>());
                return Context.Value;
            }
        }
        private IDictionary<object, object> Dic { get; set; }

        private static int _counter;
        private int _id;
        public int Id => _id;

        /// <summary>
        /// 将异步代码块放在执行的代码环境中运行, 并指定环境中的数据容器
        /// </summary>
        public static async Task RunInContextAsync(Func<Task> act, IDictionary<object, object> dic = null)
        {
            var ctx = Context.Value = new RunningContext(dic ?? new ConcurrentDictionary<object, object>());
            await act();
        }

        /// <summary>
        /// 将同步代码块放在执行的代码环境中运行, 并指定环境中的数据容器
        /// </summary>
        public static void RunInContext(Action act, IDictionary<object, object> dic = null)
        {
            RunInContextAsync(() =>
            {
                act();
                return Task.CompletedTask;
            }, dic).Wait();
        }

        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>.TryGetValue(...)
        /// </summary>
        public bool TryGetValue(object key, out object val)
        {
            return Dic.TryGetValue(key, out val);
        }
        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>.TryGetValue(...)
        /// </summary>
        public bool TryGetValue<T>(object key, out T val)
        {
            var flag = Dic.TryGetValue(key, out var tmp);
            if (flag)
            {
                val = (T)tmp;
            }
            else
            {
                val = default(T);
            }
            return flag;
        }

        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>.TrySetValue(...)
        /// </summary>
        public bool TrySetValue(object key, object val)
        {
            return Dic.TryAdd(key, val);
        }

        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>[key] = val
        /// </summary>
        public void SetValue(object key, object val)
        {
            Dic[key] = val;
        }

        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>.ContainsKey(...)
        /// </summary>
        public bool ContainsKey(object key)
        {
            return Dic.ContainsKey(key);
        }

        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>[key]
        /// </summary>
        public object GetValue(object key)
        {
            return Dic[key];
        }

        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>[key]
        /// </summary>
        public T GetValue<T>(object key)
        {
            return (T)Dic[key];
        }

        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>.Remove(...)
        /// </summary>
        public bool Remove(object key) => Dic.Remove(key);

        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>.Count
        /// </summary>
        public int Count => Dic.Count;
        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>.Clear()
        /// </summary>
        public void Clear() => Dic.Clear();
        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>.Keys.ToList()
        /// </summary>
        public List<object> Keys => Dic.Keys.ToList();
        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>.Values.ToList()
        /// </summary>
        public List<object> Values => Dic.Values.ToList();
        /// <summary>
        /// 就是对环境中的容器 IDictionary&lt;object, object>.ToList()
        /// </summary>
        public List<KeyValuePair<object, object>> All => Dic.ToList();
    }
}
